home *** CD-ROM | disk | FTP | other *** search
/ Suzy B Software 2 / Suzy B Software CD-ROM 2 (1994).iso / new_file / mintprgs / mint112s / mint112s.lzh / timeout.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-14  |  11.1 KB  |  489 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith.
  3. Copyright 1992,1993,1994 Atari Corporation.
  4. All rights reserved.
  5. */
  6.  
  7. #include "mint.h"
  8.  
  9. /*
  10.  * We initialize proc_clock to a very large value so that we don't have
  11.  * to worry about unexpected process switches while starting up
  12.  */
  13.  
  14. short proc_clock = 0x7fff;
  15.  
  16. /* used by filesystems for time/date stamps; updated once per second */
  17. short timestamp, datestamp;
  18.  
  19. extern short in_kernel;    /* in main.c */
  20.  
  21. static void unnapme P_((PROC *));
  22. static TIMEOUT    *newtimeout P_((short));
  23. static void    disposetimeout P_((TIMEOUT *));
  24. static void    inserttimeout P_ ((TIMEOUT *, long));
  25.  
  26. #define TIMEOUTS    20    /* # of static timeout structs */
  27. #define TIMEOUT_USED    0x01    /* timeout struct is in use */
  28. #define TIMEOUT_STATIC    0x02    /* this is a static timeout */
  29.  
  30. /* This gets implizitly initialized to zero, thus the flags are
  31.  * set up correctly.
  32.  */
  33. static TIMEOUT timeouts[TIMEOUTS] = { { 0, }, };
  34. TIMEOUT *tlist = NULL;
  35. TIMEOUT *expire_list = NULL;
  36.  
  37. /* Number of ticks after that an expired timeout is considered to be old
  38.    and disposed automatically.  */
  39. #define TIMEOUT_EXPIRE_LIMIT 400 /* 2 secs */
  40.  
  41. static TIMEOUT *
  42. newtimeout(fromlist)
  43.     short fromlist;
  44. {
  45.     TIMEOUT *t;
  46.     short i, sr;
  47.  
  48.     if (!fromlist) {
  49.         t = kmalloc(SIZEOF(TIMEOUT));
  50.         if (t) {
  51.             t->flags = 0;
  52.             t->arg = 0;
  53.             return t;
  54.         }
  55.     }
  56.     sr = spl7();
  57.     for (i = 0; i < TIMEOUTS; ++i) {
  58.         if (!(timeouts[i].flags & TIMEOUT_USED)) {
  59.             timeouts[i].flags |= (TIMEOUT_STATIC|TIMEOUT_USED);
  60.             spl(sr);
  61.             timeouts[i].arg = 0;
  62.             return &timeouts[i];
  63.         }
  64.     }
  65.     spl(sr);
  66.     return 0;
  67. }
  68.  
  69. static void
  70. disposetimeout(t)
  71.     TIMEOUT *t;
  72. {
  73.     if (t->flags & TIMEOUT_STATIC) t->flags &= ~TIMEOUT_USED;
  74.     else kfree(t);
  75. }
  76.  
  77. static void
  78. dispose_old_timeouts ()
  79. {
  80.   TIMEOUT *t, **prev, *old;
  81.   long now = *(long *) 0x4ba;
  82.   short sr = spl7 ();
  83.  
  84.   for (prev = &expire_list, t = *prev; t; prev = &t->next, t = *prev)
  85.     {
  86.       if (t->when < now)
  87.     {
  88.       /* This and the following timeouts are too old. Throw them away. */
  89.       *prev = 0;
  90.       spl (sr);
  91.       while (t)
  92.         {
  93.           old = t;
  94.           t = t->next;
  95.           disposetimeout (old);
  96.         }
  97.       return;
  98.     }
  99.     }
  100.   spl (sr);
  101. }
  102.  
  103. static void
  104. inserttimeout(t, delta)
  105.     TIMEOUT *t;
  106.     long delta;
  107. {
  108.     TIMEOUT **prev, *cur;
  109.     short sr = spl7();
  110.  
  111.     cur = tlist;
  112.     prev = &tlist;
  113.     while (cur) {
  114.         if (cur->when >= delta) {
  115.             cur->when -= delta;
  116.             t->next = cur;
  117.             t->when = delta;
  118.             *prev = t;
  119.             spl(sr);
  120.             return;
  121.         }
  122.         delta -= cur->when;
  123.         prev = &cur->next;
  124.         cur = cur->next;
  125.     }
  126.     assert(delta >= 0);
  127.     t->when = delta;
  128.     t->next = cur;
  129.     *prev = t;
  130.     spl(sr);
  131. }
  132.     
  133. /*
  134.  * addtimeout(long delta, void (*func)()): schedule a timeout for the current
  135.  * process, to take place in "delta" milliseconds. "func" specifies a
  136.  * function to be called at that time; the function is passed as a parameter
  137.  * the process for which the timeout was specified (i.e. the value of
  138.  * curproc at the time addtimeout() was called; note that this is probably
  139.  * *not* the current process when the timeout occurs).
  140.  *
  141.  * NOTE: if kernel memory is low, newtimeout() will try to get a statically
  142.  * allocated timeout struct (fallback method).
  143.  */
  144.  
  145. TIMEOUT * ARGS_ON_STACK
  146. addtimeout(delta, func)
  147.     long delta;
  148.     void (*func) P_((PROC *));
  149. {
  150.     TIMEOUT *t;
  151.     TIMEOUT **prev;
  152.     short sr;
  153.  
  154.     /* Try to reuse an already expired timeout that had the
  155.        same function attached */
  156.     sr = spl7();
  157.     prev = &expire_list;
  158.     for (t = *prev; t != NULL; prev = &t->next, t = *prev)
  159.       if (t->proc == curproc && t->func == func)
  160.         {
  161.           *prev = t->next;
  162.           break;
  163.         }
  164.  
  165.     spl(sr);
  166.     if (t == NULL)
  167.       t = newtimeout(0);
  168.  
  169. /* BUG: we should have some fallback mechanism for timeouts when the
  170.    kernel memory is exhausted
  171.  */
  172.     assert(t);
  173.  
  174.     t->proc = curproc;
  175.     t->func = func;
  176.     inserttimeout(t, delta);
  177.     return t;
  178. }
  179.  
  180. /*
  181.  * addroottimeout(long delta, void (*)(PROC *), short flags);
  182.  * Same as addtimeout(), except that the timeout is attached to Pid 0 (MiNT).
  183.  * This means the timeout won't be cancelled if the process which was
  184.  * running at the time addroottimeout() was called exits.
  185.  *
  186.  * Currently only bit 0 of `flags' is used. Meaning:
  187.  * Bit 0 set: Call from interrupt (cannot use kmalloc, use statically
  188.  *    allocated `struct timeout' instead).
  189.  * Bit 0 clear: Not called from interrupt, can use kmalloc.
  190.  *
  191.  * Thus addroottimeout() can be called from interrupts (bit 0 of flags set),
  192.  * which makes it *extremly* useful for device drivers.
  193.  * A serial device driver would make an addroottimeout(0, check_keys, 1)
  194.  * if some bytes have arrived.
  195.  * check_keys() is then called at the next context switch, can use all
  196.  * the kernel functions and can do time cosuming jobs.
  197.  */
  198.  
  199. TIMEOUT * ARGS_ON_STACK
  200. addroottimeout(delta, func, flags)
  201.     long delta;
  202.     void (*func) P_((PROC *));
  203.     short flags;
  204. {
  205.     TIMEOUT *t;
  206.     TIMEOUT **prev;
  207.     short sr;
  208.  
  209.     /* Try to reuse an already expired timeout that had the
  210.        same function attached */
  211.     sr = spl7();
  212.     prev = &expire_list;
  213.     for (t = *prev; t != NULL; t = *prev)
  214.       {
  215.         if (t->proc == rootproc && t->func == func)
  216.           {
  217.         *prev = t->next;
  218.         break;
  219.           }
  220.         prev = &t->next;
  221.       }
  222.     spl(sr);
  223.  
  224.     if (!t)
  225.       t = newtimeout(flags & 1);
  226.  
  227.     if (!t) return NULL;
  228.     t->proc = rootproc;
  229.     t->func = func;
  230.     inserttimeout(t, delta);
  231.     return t;
  232. }
  233.  
  234. /*
  235.  * cancelalltimeouts(): cancels all pending timeouts for the current
  236.  * process
  237.  */
  238.  
  239. void ARGS_ON_STACK
  240. cancelalltimeouts()
  241. {
  242.     TIMEOUT *cur, **prev, *old;
  243.     long delta;
  244.     short sr = spl7 ();
  245.  
  246.     cur = tlist;
  247.     prev = &tlist;
  248.     while (cur) {
  249.         if (cur->proc == curproc) {
  250.             delta = cur->when;
  251.             old = cur;
  252.             *prev = cur = cur->next;
  253.             if (cur) cur->when += delta;
  254.             spl(sr);
  255.             disposetimeout(old);
  256.             sr = spl7();
  257.         /* ++kay: just in case an interrupt handler installed a
  258.          * timeout right after `prev' and before `cur' */
  259.             cur = *prev;
  260.         }
  261.         else {
  262.             prev = &cur->next;
  263.             cur = cur->next;
  264.         }
  265.     }
  266.     prev = &expire_list;
  267.     for (cur = *prev; cur; cur = *prev)
  268.       {
  269.         if (cur->proc == curproc)
  270.           {
  271.         *prev = cur->next;
  272.         spl (sr);
  273.         disposetimeout (cur);
  274.         sr = spl7 ();
  275.           }
  276.         else
  277.           prev = &cur->next;
  278.       }
  279.     spl (sr);
  280. }
  281.  
  282. /*
  283.  * Cancel a specific timeout. If the timeout isn't on the list, or isn't
  284.  * for this process, we do nothing; otherwise, we cancel the time out
  285.  * and then free the memory it used. *NOTE*: it's very possible (indeed
  286.  * likely) that "this" was already removed from the list and disposed of
  287.  * by the timeout processing routines, so it's important that we check
  288.  * for it's presence in the list and do absolutely nothing if we don't
  289.  * find it there!
  290.  */
  291.  
  292. void ARGS_ON_STACK
  293. canceltimeout(this)
  294.     TIMEOUT *this;
  295. {
  296.     TIMEOUT *cur, **prev;
  297.     short sr = spl7();
  298.  
  299.     /* First look at the list of expired timeouts */
  300.     prev = &expire_list;
  301.     for (cur = *prev; cur; cur = *prev)
  302.       {
  303.         if (cur == this && cur->proc == curproc)
  304.           {
  305.         *prev = cur->next;
  306.         spl (sr);
  307.         disposetimeout (this);
  308.         return;
  309.           }
  310.         prev = &cur->next;
  311.       }
  312.  
  313.     prev = &tlist;
  314.     for (cur = tlist; cur; cur = cur->next) {
  315.         if (cur == this && cur->proc == curproc) {
  316.             *prev = cur->next;
  317.             if (cur->next) {
  318.                 cur->next->when += this->when;
  319.             }
  320.             spl (sr);
  321.             disposetimeout(this);
  322.             return;
  323.         }
  324.         prev = &cur->next;
  325.     }
  326.     spl(sr);
  327. }
  328.  
  329. void ARGS_ON_STACK
  330. cancelroottimeout(this)
  331.     TIMEOUT *this;
  332. {
  333.     TIMEOUT *cur, **prev;
  334.     short sr = spl7();
  335.  
  336.     /* First look at the list of expired timeouts */
  337.     prev = &expire_list;
  338.     for (cur = *prev; cur; cur = *prev)
  339.       {
  340.         if (cur == this && cur->proc == rootproc)
  341.           {
  342.         *prev = cur->next;
  343.         spl (sr);
  344.         disposetimeout (this);
  345.         return;
  346.           }
  347.         prev = &cur->next;
  348.       }
  349.     
  350.     prev = &tlist;
  351.     for (cur = tlist; cur; cur = cur->next) {
  352.         if (cur == this && (cur->proc == rootproc)) {
  353.             *prev = cur->next;
  354.             if (cur->next) {
  355.                 cur->next->when += this->when;
  356.             }
  357.             spl (sr);
  358.             disposetimeout(this);
  359.             return;
  360.         }
  361.         prev = &cur->next;
  362.     }
  363.     spl(sr);
  364. }
  365.  
  366. /*
  367.  * timeout: called every 20 ms or so by GEMDOS, this routine
  368.  * is responsible for maintaining process times and such.
  369.  * it should also decrement the "proc_clock" variable, but
  370.  * should *not* take any action when it reaches 0 (the state of the
  371.  * stack is too uncertain, and time is too critical). Instead,
  372.  * a vbl routine checks periodically and if "proc_clock" is 0
  373.  * suspends the current process
  374.  */
  375.  
  376. volatile int our_clock = 1000;
  377.  
  378. /* variables for monitoring the keyboard */
  379. extern IOREC_T    *keyrec;    /* keyboard i/o record pointer */
  380. extern short    kintr;        /* keyboard interrupt pending (see intr.s) */
  381.  
  382. void ARGS_ON_STACK
  383. timeout()
  384. {
  385.     int ms;        /* time between ticks */
  386.  
  387.     kintr = keyrec->head != keyrec->tail;
  388.  
  389.     ms = *((short *)0x442L);
  390.     if (proc_clock > 0)
  391.         proc_clock--;
  392.  
  393.     our_clock -= ms;
  394.     if (tlist) {
  395.         tlist->when -= ms;
  396.     }
  397. }
  398.  
  399. /*
  400.  * sleep() calls this routine to check on alarms and other sorts
  401.  * of time-outs on every context switch.
  402.  */
  403.  
  404. void
  405. checkalarms()
  406. {
  407.     extern long searchtime;        /* in dosdir.c */
  408.     PROC *p;
  409.     long delta;
  410.     void (*evnt) P_((PROC *, long arg));
  411.     TIMEOUT *old;
  412.     short sr;
  413.     long arg;
  414.  
  415. /* do the once per second things */
  416.     while (our_clock < 0) {
  417.         our_clock += 1000;
  418.         timestamp = Tgettime();
  419.         datestamp = Tgetdate();
  420.         searchtime++;
  421.         reset_priorities();
  422.     }
  423.  
  424.     sr = spl7();
  425. /* see if there are outstanding timeout requests to do */
  426.     while (tlist && ((delta = tlist->when) <= 0)) {
  427.         p = tlist->proc;
  428. /* hack: pass an extra long as arg, those intrested in it will need
  429.  * a cast and have to place it in t->arg themselves but that way
  430.  * everything else still works without change    -nox */
  431.         arg = tlist->arg;
  432.         evnt = (void (*)P_((PROC *, long)))tlist->func;
  433.         old = tlist;
  434.         tlist = tlist->next;
  435. /* if delta < 0, it's possible that the time has come for the next timeout
  436.  * to occur.
  437.  * ++kay: moved this before the timeout fuction is called, in case the
  438.  * timeout function installes a new timeout. */
  439.         if (tlist)
  440.             tlist->when += delta;
  441.         old->next = expire_list;
  442.         old->when = *(long *) 0x4ba + TIMEOUT_EXPIRE_LIMIT;
  443.         expire_list = old;
  444.         spl(sr);
  445. /* ++kay: debug output at spl7 hangs the system, so moved it here */
  446.         TRACE(("doing timeout code for pid %d", p->pid));
  447.  
  448.     /* call the timeout function */
  449.         (*evnt)(p, arg);
  450.         sr = spl7();
  451.     }
  452.     spl(sr);
  453.     /* Now look at the expired timeouts if some are getting old */
  454.     dispose_old_timeouts ();
  455. }
  456.  
  457. /*
  458.  * nap(n): nap for n milliseconds. Used in loops where we're waiting for
  459.  * an event. If we expect the event *very* soon, we should use yield
  460.  * instead.
  461.  * NOTE: we may not sleep for exactly n milliseconds; signals can wake
  462.  * us earlier, and the vagaries of process scheduling may cause us to
  463.  * oversleep...
  464.  */
  465.  
  466. static void
  467. unnapme(p)
  468.     PROC *p;
  469. {
  470.     if (p->wait_q == SELECT_Q && p->wait_cond == (long)nap) {
  471.         short sr = spl7();
  472.         rm_q(SELECT_Q, p);
  473.         add_q(READY_Q, p);
  474.         spl(sr);
  475.         p->wait_cond = 0;
  476.     }
  477. }
  478.  
  479. void ARGS_ON_STACK 
  480. nap(n)
  481.     unsigned n;
  482. {
  483.     TIMEOUT *t;
  484.  
  485.     t = addtimeout((long)n, unnapme);
  486.     sleep(SELECT_Q, (long)nap);
  487.     canceltimeout(t);
  488. }
  489.